#include <UnitTest++.h>
#include "ray.h"

using namespace mcrx;

// generates a normalized direction vector
struct construction_fixture {
  construction_fixture() {
    dir=vec3d(.5,1,2);
    dir/=sqrt(dot(dir,dir));
  }

  vec3d dir;
};


TEST_FIXTURE(construction_fixture, defaultconstruction)
{
  ray_base r;
  // default constructor doesn't initialize anything

  //CHECK_ARRAY_EQUAL(r.position(), vec3d(0,0,0), 3);
  //CHECK_ARRAY_EQUAL(r.direction(), vec3d(0,0,0), 3);
  //CHECK_EQUAL(r.length(), 0.0);
  //CHECK_EQUAL(r.scatterings(), 0);
}

TEST_FIXTURE(construction_fixture, construction1)
{
  ray_base r(vec3d(0,0,0), dir);

  CHECK_ARRAY_EQUAL(r.position(), vec3d(0,0,0), 3);
  CHECK_ARRAY_EQUAL(r.direction(), dir, 3);
  CHECK_ARRAY_CLOSE(r.inverse_direction(), 1./dir, 3, 1e-10);
  CHECK_EQUAL(r.length(), 0.0);
  CHECK_EQUAL(r.scatterings(), 0);
  CHECK_EQUAL(r.doppler(), 1.0);
  CHECK_EQUAL(r.traversed_column().size(), 0);
}

TEST_FIXTURE(construction_fixture, construction2)
{
  T_densities d(2);
  d=42.0;
  ray_base r(vec3d(0,0,0), dir, d);

  CHECK_ARRAY_EQUAL(r.position(), vec3d(0,0,0), 3);
  CHECK_ARRAY_EQUAL(r.direction(), dir, 3);
  CHECK_ARRAY_CLOSE(r.inverse_direction(), 1./dir, 3, 1e-10);
  CHECK_EQUAL(r.length(), 0.0);
  CHECK_EQUAL(r.scatterings(), 0);
  CHECK_EQUAL(r.doppler(), 1.0);
  CHECK_EQUAL(r.traversed_column().size(), 2);
  CHECK(all(r.traversed_column()==d));
  // should be a reference copy
  d(0)=43;
  CHECK_EQUAL(r.traversed_column()(0), d(0));
}

TEST_FIXTURE(construction_fixture, copy_construction)
{
  T_densities d(2);
  d=42.0;
  T_densities dd(d.copy());
  ray_base r(vec3d(1,2,3), dir, dd);
  // make it be nontrivial so we can test proper copying of these fields
  r.propagate(4.0, d);
  d+=d;
  ++r.scatterings();
  CHECK_EQUAL(r.scatterings(), 1);
  CHECK_ARRAY_CLOSE(d.dataFirst(),r.traversed_column().dataFirst(),2,1e-10);

  ray_base rr(r);
  CHECK_ARRAY_CLOSE(rr.position(), vec3d(vec3d(1,2,3)+dir*4.0), 3, 1e-10);
  CHECK_ARRAY_EQUAL(rr.direction(), dir, 3);
  CHECK_ARRAY_CLOSE(rr.inverse_direction(), 1./dir, 3, 1e-10);
  CHECK_CLOSE(rr.length(), 4.0, 1e-10);
  CHECK_EQUAL(rr.scatterings(), 1);
  CHECK_EQUAL(rr.doppler(), 1.0);
  CHECK_ARRAY_CLOSE(d.dataFirst(),rr.traversed_column().dataFirst(),2,1e-10);
}

TEST(column)
{
  ray_base r;
  T_densities d(2), dd(2);
  d=42;
  
  r.set_column_ref(d);

  CHECK_EQUAL(2, r.traversed_column().size());
  CHECK_ARRAY_EQUAL(d.dataFirst(), r.traversed_column().dataFirst(), 2);

  // should be a reference copy
  CHECK(&r.traversed_column()(0) == &d(0));
  d(0)=43;
  CHECK_EQUAL(d(0), r.traversed_column()(0));

  r.reset_traversed_column();
  CHECK(all(r.traversed_column()==0));
  dd=4711;
  r.set_traversed_column(dd);
  CHECK(all(r.traversed_column()==dd));
}

TEST(doppler)
{
  ray_base r;
  r.set_doppler(1.1);
  CHECK_EQUAL(1.1, r.doppler());
  r.add_doppler(1.1);
  CHECK_CLOSE(1.1*1.1, r.doppler(), 1e-10);
}

TEST(scatterings)
{
  ray_base r;
  r.scatterings()=0;
  CHECK_EQUAL(0, r.scatterings());
  ++r.scatterings();
  CHECK_EQUAL(1, r.scatterings());
}

// sets up a nontrivial ray_base object
struct nontrivial_ray_base_fixture {
  nontrivial_ray_base_fixture() : 
    d1(2), d2(2), r(vec3d(1,2,3), cf.dir, d1) {
    d1=42.0;
    d2=4711.0;
    ++r.scatterings();
    r.propagate(12.0);
    r.set_doppler(1.1);
  }

  T_densities d1,d2;
  construction_fixture cf;
  ray_base r;
};

TEST_FIXTURE(nontrivial_ray_base_fixture, assignment)
{
  ray_base rr;
  rr.set_column_ref(d2);

  rr=r;

  CHECK_ARRAY_EQUAL(r.position(), rr.position(), 3);
  CHECK_ARRAY_EQUAL(r.direction(), rr.direction(), 3);
  CHECK_ARRAY_EQUAL(r.inverse_direction(), rr.inverse_direction(), 3);
  CHECK_EQUAL(r.length(), rr.length());
  CHECK_EQUAL(r.doppler(), rr.doppler());
  CHECK_EQUAL(r.scatterings(), rr.scatterings());
  CHECK_ARRAY_EQUAL(r.traversed_column().dataFirst(), 
		    rr.traversed_column().dataFirst(), 2);
  // check that it did not reference copy
  CHECK(&r.traversed_column()(0) != &rr.traversed_column()(0));
}


TEST_FIXTURE(nontrivial_ray_base_fixture, deep_assignment)
{
  ray_base rr;

  rr.deep_assign(r);

  CHECK_ARRAY_EQUAL(r.position(), rr.position(), 3);
  CHECK_ARRAY_EQUAL(r.direction(), rr.direction(), 3);
  CHECK_ARRAY_EQUAL(r.inverse_direction(), rr.inverse_direction(), 3);
  CHECK_EQUAL(r.length(), rr.length());
  CHECK_EQUAL(r.doppler(), rr.doppler());
  CHECK_EQUAL(r.scatterings(), rr.scatterings());
  CHECK_ARRAY_EQUAL(r.traversed_column().dataFirst(), 
		    rr.traversed_column().dataFirst(), 2);
  // check that it did not reference copy
  rr.reset_traversed_column();
  CHECK(all(rr.traversed_column()==0));
  CHECK_ARRAY_EQUAL(d1.dataFirst(), r.traversed_column().dataFirst(), 2);
}

TEST_FIXTURE(nontrivial_ray_base_fixture, shallow_assignment)
{
  ray_base rr;

  rr.shallow_assign(r);

  CHECK_ARRAY_EQUAL(r.position(), rr.position(), 3);
  CHECK_ARRAY_EQUAL(r.direction(), rr.direction(), 3);
  CHECK_ARRAY_EQUAL(r.inverse_direction(), rr.inverse_direction(), 3);
  CHECK_EQUAL(r.length(), rr.length());
  CHECK_EQUAL(r.doppler(), rr.doppler());
  CHECK_EQUAL(r.scatterings(), rr.scatterings());
  CHECK_ARRAY_EQUAL(r.traversed_column().dataFirst(), 
		    rr.traversed_column().dataFirst(), 2);
  // check that it did reference copy
  CHECK_EQUAL(rr.traversed_column().dataFirst(), 
	      r.traversed_column().dataFirst());
  rr.reset_traversed_column();
  CHECK(all(r.traversed_column()==0));
}

TEST_FIXTURE(nontrivial_ray_base_fixture, propagation1)
{
  r.set_position(vec3d(0,0,0));
  r.reset_traversed_column();
  r.propagate(10.0);

  CHECK_ARRAY_CLOSE(10*r.direction(), r.position(), 3, 1e-10);
  CHECK(all(r.traversed_column()==0));
  CHECK_CLOSE(10.0, r.length(), 1e-10);
}

TEST_FIXTURE(nontrivial_ray_base_fixture, propagation2)
{
  r.set_position(vec3d(0,0,0));
  r.reset_traversed_column();
  r.propagate(10.0,d2);

  CHECK_ARRAY_CLOSE(10*r.direction(), r.position(), 3, 1e-10);
  CHECK(all(r.traversed_column()==d2));
  CHECK_CLOSE(10.0, r.length(), 1e-10);
}

TEST_FIXTURE(nontrivial_ray_base_fixture, propagation3)
{
  r.set_position(vec3d(0,0,0));
  r.reset_traversed_column();
  r.propagate(10.0,2*d2);

  CHECK_ARRAY_CLOSE(10*r.direction(), r.position(), 3, 1e-10);
  CHECK(all(r.traversed_column()==2*d2));
  CHECK_CLOSE(10.0, r.length(), 1e-10);
}
